# -*- coding: utf-8 -*-
#
# loader.py
#
#
# PyEMOF: An EMOF support for Python
#
# Copyright (C) 2004, 2007 Raphael Marvie 
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
# 02111-1307, USA.
# http://www.fsf.org/licensing/licenses/gpl.html
#
# Author contact: raphael.marvie@lifl.fr
#
# Modified and adapted to conform with ECORE 2.0 by brenomachado@gmail.com

from xml.dom import minidom

import emof.core

def gen():
    i = 0
    while True:
        yield i
        i += 1

x = gen()

class LoadElement (object):

    def __init__(self, post, ids):
        super(LoadElement, self).__init__()
        self._post = post
        self._ids = ids

    def create(self, elt):
        pass

    def process(self, obj, elt):
        pass

    def __call__(self, elt):
        obj = self.create(elt)
        self._post['stack'].append(obj._xmi_id)
        self.process(obj, elt)
        self._post['stack'].remove(obj._xmi_id)
        return obj


class Element (LoadElement):

    def __init__(self, post, ids):
        super(Element, self).__init__(post, ids)

    def create(self, elt):
        obj = emof.core.Element()
        obj._xmi_id = elt.getAttribute('xmi:id')
        self._ids[obj._xmi_id] = obj
        if len(obj._xmi_id.split(":")) > 1:
            obj._id = obj._xmi_id.split(":")[1]
        else:
            obj._id = x.next()
        return obj
    
    def process(self, obj, elt):
        
        super(Element, self).process(obj, elt)
        e = elt.firstChild
        while e:
            if e.nodeName == u'ownedComment':
                #id = e.firstChild.data.strip()
                id = e.getAttribute('idref')
                self._post['append'].append((obj, 'ownedComment', id))
            e = e.nextSibling
        e = elt.firstChild
        while e:
            if e.nodeName == u'tag':
                #id = e.firstChild.data.strip()
                id = e.getAttribute('idref')
                self._post['append'].append((obj, 'tag', id))
            e = e.nextSibling
        
        

class NamedElement (Element):

    def __init__(self, post, ids):
        super(NamedElement, self).__init__(post, ids)

    def create(self, elt):
        obj = emof.core.NamedElement()
        obj._xmi_id = elt.getAttribute('xmi:id')
        self._ids[obj._xmi_id] = obj
        if len(obj._xmi_id.split(":")) > 1:
            obj._id = obj._xmi_id.split(":")[1]
        else:
            obj._id = x.next()
        return obj

    def process(self, obj, elt):
        super(NamedElement, self).process(obj, elt)
        if elt.getAttribute('name'):
            obj.name = elt.getAttribute('name')

class Comment (Element):

    def __init__(self, post, ids, fact):
        super(Comment, self).__init__(post, ids)
        self.fact = fact

    def create(self, elt):
        obj = self.fact.create_Comment()
        obj._xmi_id = elt.getAttribute('xmi:id')
        self._ids[obj._xmi_id] = obj
        if len(obj._xmi_id.split(":")) > 1:
            obj._id = obj._xmi_id.split(":")[1]
        else:
            obj._id = x.next()
        return obj

    def process(self, obj, elt):
        super(Comment, self).process(obj, elt)
        if elt.getAttribute('body'):
            obj.body = elt.getAttribute('body')
        e = elt.firstChild
        while e:
            if e.nodeName == u'annotatedElement':
                #id = e.firstChild.data.strip()
                id = e.getAttribute('idref')
                self._post['append'].append((obj, 'annotatedElement', id))
            e = e.nextSibling
            
class Tag (Element):

    def __init__(self, post, ids, fact):
        super(Tag, self).__init__(post, ids)
        self.fact = fact

    def create(self, elt):
        obj = self.fact.create_Tag()
        obj._xmi_id = elt.getAttribute('xmi:id')
        self._ids[obj._xmi_id] = obj
        if len(obj._xmi_id.split(":")) > 1:
            obj._id = obj._xmi_id.split(":")[1]
        else:
            obj._id = x.next()
        return obj

    def process(self, obj, elt):
        super(Tag, self).process(obj, elt)
        if elt.getAttribute('name'):
            obj.name = elt.getAttribute('name')
        if elt.getAttribute('value'):
            obj.value = elt.getAttribute('value')
        e = elt.firstChild
        while e:
            if e.nodeName == u'element':
                #id = e.firstChild.data.strip()
                id = e.getAttribute('idref')
                self._post['append'].append((obj, 'element', id))
            e = e.nextSibling

#OK
class Package (NamedElement):

    def __init__(self, post, ids, fact):
        super(Package, self).__init__(post, ids)
        self.fact= fact

    def create(self, elt):
        obj = self.fact.create_Package()
        obj._xmi_id = elt.getAttribute('xmi:id')
        self._ids[obj._xmi_id] = obj
        if len(obj._xmi_id.split(":")) > 1:
            obj._id = obj._xmi_id.split(":")[1]
        else:
            obj._id = x.next()
        return obj

    def process(self, obj, elt):
        super(Package, self).process(obj, elt)
        if elt.getAttribute('uri'):
            obj.uri = elt.getAttribute('uri')
        e = elt.firstChild
        while e:
            if e.nodeName == u'nestedPackage':
                clss = loaders['emof:Package']
                o = clss(self._post, self._ids, self.fact)(e)
                obj.nestedPackage.append(o)
            e = e.nextSibling
        id = elt.getAttribute('nestingPackage')
        
        if id:
            self._post['set'].append((obj, '_nestingPackage', id))
        #***********
        else:
            if len(self._post['stack']) - 1 > 0:
                self._post['set'].append((obj, '_nestingPackage', self._post['stack'][len(self._post['stack']) - 2]))
            
        e = elt.firstChild
        while e:
            if e.nodeName == u'ownedType':
                clss = loaders[e.getAttribute('xmi:type')]
                o = clss(self._post, self._ids, self.fact)(e)
                obj.ownedType.append(o)
            e = e.nextSibling

class TypedElement (NamedElement):

    def __init__(self, post, ids):
        super(TypedElement, self).__init__(post, ids)

    def create(self, elt):
        obj = emof.core.TypedElement()
        obj._xmi_id = elt.getAttribute('xmi:id')
        self._ids[obj._xmi_id] = obj
        if len(obj._xmi_id.split(":")) > 1:
            obj._id = obj._xmi_id.split(":")[1]
        else:
            obj._id = x.next()
        return obj

    def process(self, obj, elt):
        super(TypedElement, self).process(obj, elt)
        id = elt.getAttribute('type')
        if id:
            self._post['set'].append((obj, '_type', id))
        else:
            if len(self._post['stack']) - 1 > 0:
                self._post['set'].append((obj, '_type', self._post['stack'][len(self._post['stack']) - 2]))    


#OK
class EnumerationLiteral (NamedElement):

    def __init__(self, post, ids, fact):
        super(EnumerationLiteral, self).__init__(post, ids)
        self.fact = fact

    def create(self, elt):
        obj = self.fact.create_EnumerationLiteral()
        obj._xmi_id = elt.getAttribute('xmi:id')
        self._ids[obj._xmi_id] = obj
        if len(obj._xmi_id.split(":")) > 1:
            obj._id = obj._xmi_id.split(":")[1]
        else:
            obj._id = x.next()
        return obj

    def process(self, obj, elt):
        super(EnumerationLiteral, self).process(obj, elt)
        id = elt.getAttribute('enumeration')
        if id:
            self._post['set'].append((obj, '_enumeration', id))
        else:
            if len(self._post['stack']) - 1 > 0:
                self._post['set'].append((obj, '_enumeration', self._post['stack'][len(self._post['stack']) - 2]))
            


class Type (NamedElement):

    def __init__(self, post, ids):
        super(Type, self).__init__(post, ids)

    def create(self, elt):
        obj = emof.core.Type()
        obj._xmi_id = elt.getAttribute('xmi:id')
        self._ids[obj._xmi_id] = obj
        if len(obj._xmi_id.split(":")) > 1:
            obj._id = obj._xmi_id.split(":")[1]
        else:
            obj._id = x.next()
        return obj

    def process(self, obj, elt):
        super(Type, self).process(obj, elt)
        id = elt.getAttribute('package')
        if id:
            self._post['set'].append((obj, 'package', id))
        else:
            if len(self._post['stack']) - 1 > 0:
                self._post['set'].append((obj, '_package', self._post['stack'][len(self._post['stack']) - 2]))            


class MultiplicityElement (TypedElement):

    def __init__(self, post, ids):
        super(MultiplicityElement, self).__init__(post, ids)

    def create(self, elt):
        obj = emof.core.MultiplicityElement()
        obj._xmi_id = elt.getAttribute('xmi:id')
        self._ids[obj._xmi_id] = obj
        if len(obj._xmi_id.split(":")) > 1:
            obj._id = obj._xmi_id.split(":")[1]
        else:
            obj._id = x.next()
        return obj

    def process(self, obj, elt):
        super(MultiplicityElement, self).process(obj, elt)
        if elt.getAttribute('isOrdered'):
            obj.isOrdered = elt.getAttribute('isOrdered')
        if elt.getAttribute('isUnique'):
            obj.isUnique = elt.getAttribute('isUnique')
        if elt.getAttribute('lower'):
            obj.lower = elt.getAttribute('lower')
        if elt.getAttribute('upper'):
            obj.upper = elt.getAttribute('upper')


class DataType (Type):

    def __init__(self, post, ids):
        super(DataType, self).__init__(post, ids)

    def create(self, elt):
        obj = emof.core.DataType()
        obj._xmi_id = elt.getAttribute('xmi:id')
        self._ids[obj._xmi_id] = obj
        if len(obj._xmi_id.split(":")) > 1:
            obj._id = obj._xmi_id.split(":")[1]
        else:
            obj._id = x.next()
        return obj

    def process(self, obj, elt):
        super(DataType, self).process(obj, elt)

class Class (Type):

    def __init__(self, post, ids, fact):
        super(Class, self).__init__(post, ids)
        self.fact= fact

    def create(self, elt):
        obj = self.fact.create_Class()
        obj._xmi_id = elt.getAttribute('xmi:id')
        self._ids[obj._xmi_id] = obj
        if len(obj._xmi_id.split(":")) > 1:
            obj._id = obj._xmi_id.split(":")[1]
        else:
            obj._id = x.next()
        return obj

    def process(self, obj, elt):
        super(Class, self).process(obj, elt)
        if elt.getAttribute('isAbstract'):
            obj.isAbstract = elt.getAttribute('isAbstract')
        e = elt.firstChild
        while e:
            if e.nodeName == u'ownedAttribute':
                clss = loaders['emof:Property']
                o = clss(self._post, self._ids, self.fact)(e)
                obj.ownedAttribute.append(o)
            e = e.nextSibling
        e = elt.firstChild
        while e:
            if e.nodeName == u'ownedOperation':
                clss = loaders['emof:Operation']
                o = clss(self._post, self._ids, self.fact)(e)
                obj.ownedOperation.append(o)
            e = e.nextSibling
        e = elt.firstChild
            
        if elt.getAttribute('superClass'):
            sp = elt.getAttribute('superClass')
            for id in sp.split(' '):
                self._post['append'].append((obj, 'superClass', id))
        #while e:
        #    if e.nodeName == u'superClass':
        #        #id = e.firstChild.data.strip()
        #        id = e.getAttribute('idref')
        #        self._post['append'].append((obj, 'superClass', id))
        #    e = e.nextSibling


class Operation (MultiplicityElement):

    def __init__(self, post, ids, fact):
        super(Operation, self).__init__(post, ids)
        self.fact = fact

    def create(self, elt):
        obj = self.fact.create_Operation()
        obj._xmi_id = elt.getAttribute('xmi:id')
        self._ids[obj._xmi_id] = obj
        if len(obj._xmi_id.split(":")) > 1:
            obj._id = obj._xmi_id.split(":")[1]
        else:
            obj._id = x.next()
        return obj

    def process(self, obj, elt):
        super(Operation, self).process(obj, elt)
        e = elt.firstChild
        while e:
            if e.nodeName == u'raisedException':
                #id = e.firstChild.data.strip()
                id = e.getAttribute('idref')
                self._post['append'].append((obj, 'raisedException', id))
            e = e.nextSibling
        e = elt.firstChild
        while e:
            if e.nodeName == u'ownedParameter':
                clss = loaders['emof:Parameter']
                o = clss(self._post, self._ids, self.fact)(e)
                obj.ownedParameter.append(o)
            e = e.nextSibling
        id = elt.getAttribute('class')
        if id:
            self._post['set'].append((obj, '_class_', id))
        else:
            if len(self._post['stack']) - 1 > 0:
                self._post['set'].append((obj, '_class_', self._post['stack'][len(self._post['stack']) - 2]))


class Parameter (MultiplicityElement):

    def __init__(self, post, ids, fact):
        super(Parameter, self).__init__(post, ids)
        self.fact = fact

    def create(self, elt):
        obj = self.fact.create_Parameter()
        obj._xmi_id = elt.getAttribute('xmi:id')
        self._ids[obj._xmi_id] = obj
        if len(obj._xmi_id.split(":")) > 1:
            obj._id = obj._xmi_id.split(":")[1]
        else:
            obj._id = x.next()
        return obj

    def process(self, obj, elt):
        super(Parameter, self).process(obj, elt)
        e = elt.firstChild
        id = elt.getAttribute('operation')
        if id:
            self._post['set'].append((obj, '_operation', id))
        else:
            if len(self._post['stack']) - 1 > 0:
                self._post['set'].append((obj, '_operation', self._post['stack'][len(self._post['stack']) - 2]))
                            
        id = elt.getAttribute('type')
        if id:
            self._post['set'].append((obj, '_type', id))
        else:
            if len(self._post['stack']) - 1 > 0:
                self._post['set'].append((obj, '_type', self._post['stack'][len(self._post['stack']) - 2]))

class Property (MultiplicityElement):

    def __init__(self, post, ids, fact):
        super(Property, self).__init__(post, ids)
        self.fact = fact

    def create(self, elt):
        obj = self.fact.create_Property()
        obj._xmi_id = elt.getAttribute('xmi:id')
        self._ids[obj._xmi_id] = obj
        if len(obj._xmi_id.split(":")) > 1:
            obj._id = obj._xmi_id.split(":")[1]
        else:
            obj._id = x.next()
        return obj

    def process(self, obj, elt):
        super(Property, self).process(obj, elt)
        if elt.getAttribute('isReadOnly'):
            obj.isReadOnly = elt.getAttribute('isReadOnly')
        if elt.getAttribute('default'):
            obj.default = elt.getAttribute('default')
        if elt.getAttribute('isComposite'):
            obj.isComposite = elt.getAttribute('isComposite')
        if elt.getAttribute('isDerived'):
            obj.isDerived = elt.getAttribute('isDerived')
        if elt.getAttribute('isId'):
            obj.isId = elt.getAttribute('isId')
        id = elt.getAttribute('opposite')
        if id:
            self._post['set'].append((obj, '_opposite', id))
        else:
            if len(self._post['stack']) - 1 > 0:
                self._post['set'].append((obj, '_opposite', self._post['stack'][len(self._post['stack']) - 2]))
                
        id = elt.getAttribute('class')
        if id:
            self._post['set'].append((obj, '_class_', id))
        else:
            if len(self._post['stack']) - 1 > 0:
                self._post['set'].append((obj, '_class_', self._post['stack'][len(self._post['stack']) - 2]))
        id = elt.getAttribute('type')
        if id:
            self._post['set'].append((obj, '_type', id))
        else:
            if len(self._post['stack']) - 1 > 0:
                self._post['set'].append((obj, '_type', self._post['stack'][len(self._post['stack']) - 2]))

#OK
class Enumeration (DataType):

    def __init__(self, post, ids, fact):
        super(Enumeration, self).__init__(post, ids)
        self.fact = fact

    def create(self, elt):
        obj = self.fact.create_Enumeration()
        obj._xmi_id = elt.getAttribute('xmi:id')
        self._ids[obj._xmi_id] = obj
        if len(obj._xmi_id.split(":")) > 1:
            obj._id = obj._xmi_id.split(":")[1]
        else:
            obj._id = x.next()
        return obj

    def process(self, obj, elt):
        super(Enumeration, self).process(obj, elt)
        e = elt.firstChild
        while e:
            if e.nodeName == u'ownedLiteral':
                clss = loaders['emof:EnumerationLiteral']
                o = clss(self._post, self._ids, self.fact)(e)
                obj.ownedLiteral.append(o)
            e = e.nextSibling


class PrimitiveType (DataType):

    def __init__(self, post, ids, fact):
        super(PrimitiveType, self).__init__(post, ids)
        self.fact = fact

    def create(self, elt):
        obj = self.fact.create_PrimitiveType()
        obj._xmi_id = elt.getAttribute('xmi:id')
        self._ids[obj._xmi_id] = obj
        if len(obj._xmi_id.split(":")) > 1:
            obj._id = obj._xmi_id.split(":")[1]
        else:
            obj._id = x.next()
        return obj

    def process(self, obj, elt):
        super(PrimitiveType, self).process(obj, elt)


class Integer (LoadElement):

    def __init__(self, post, ids, fact):
        super(Integer, self).__init__(post, ids)
        self.fact= fact

    def create(self, elt):
        #data = elt.firstChild.data.strip()
        obj = self.fact.create_Integer()
        obj._xmi_id = elt.getAttribute('xmi:id')
        self._ids[obj._xmi_id] = obj
        if len(obj._xmi_id.split(":")) > 1:
            obj._id = obj._xmi_id.split(":")[1]
        else:
            obj._id = x.next()
        return obj


class Boolean (LoadElement):

    def __init__(self, post, ids, fact):
        super(Boolean, self).__init__(post, ids)
        self.fact= fact

    def create(self, elt):
        obj = self.fact.create_Boolean()
        obj._xmi_id = elt.getAttribute('xmi:id')
        self._ids[obj._xmi_id] = obj
        if len(obj._xmi_id.split(":")) > 1:
            obj._id = obj._xmi_id.split(":")[1]
        else:
            obj._id = x.next()
        return obj

class String (LoadElement):

    def __init__(self, post, ids,fact):
        super(String, self).__init__(post, ids)
        self.fact= fact

    def create(self, elt):
        obj = self.fact.create_String()
        obj._xmi_id = elt.getAttribute('xmi:id')
        self._ids[obj._xmi_id] = obj
        if len(obj._xmi_id.split(":")) > 1:
            obj._id = obj._xmi_id.split(":")[1]
        else:
            obj._id = x.next()
        return obj

class UnlimitedNatural (LoadElement):

    def __init__(self, post, ids, fact):
        super(UnlimitedNatural, self).__init__(post, ids)
        self.fact= fact

    def create(self, elt):
        obj = self.fact.create_UnlimitedNatural()
        obj._xmi_id = elt.getAttribute('xmi:id')
        self._ids[obj._xmi_id] = obj
        if len(obj._xmi_id.split(":")) > 1:
            obj._id = obj._xmi_id.split(":")[1]
        else:
            obj._id = x.next()
        return obj        
        
loaders = {
    "emof:Element": Element,
    "emof:Tag": Tag,
    "emof:Package": Package,
    "emof:Comment": Comment,
    "emof:EnumerationLiteral": EnumerationLiteral,
    "emof:Class": Class,
    "emof:Operation": Operation,
    "emof:Parameter": Parameter,
    "emof:Property": Property,
    "emof:Enumeration": Enumeration,
    "emof:PrimitiveType": PrimitiveType,
    "emof:Integer": Integer,
    "emof:Boolean": Boolean,
    "emof:String": String,
    "emof:UnlimitedNatural": UnlimitedNatural    
}


class Loader (object):
    '''emof model loader from an XMI file to a repository.'''

    def __init__(self):
        super(Loader, self).__init__()
        self._post = dict()
        self._post['append'] = list()
        self._post['set'] = list()
        self._post['stack'] = list()
        self._ids = dict()

    def _postprocessing(self):
        for obj, attr, id_ in self._post['append']:
            a = getattr(obj, attr)
            a.append(self._ids[id_])
        for obj, attr, id_ in self._post['set']:
            setattr(obj, attr, self._ids[id_])

    def load(self, filename):
        doc = minidom.parseString(filename)
        xmi = doc.documentElement
        assert xmi.nodeName == 'xmi:XMI' or \
                (xmi.nodeName == 'emof:Package' and \
                 unicode(xmi.getAttribute('xmlns:emof')) == \
                 'http://schema.omg.org/spec/MOF/2.0/emof.xml'), \
                 'Bad file format'
                 
        if (xmi.nodeName == 'emof:Package' and \
                 unicode(xmi.getAttribute('xmlns:emof')) == \
                 'http://schema.omg.org/spec/MOF/2.0/emof.xml'):
            xmit = minidom.Document()
            root = xmit.createElement('xmi:XMI')
            root.setAttribute('xmi:version', '2.1')
            root.setAttribute('xmlns:xmi', 'http://www.omg.org/XMI')
            root.setAttribute('xmlns:emof',
                              'http://schema.omg.org/spec/MOF/2.0/emof.xml')
            root.appendChild(xmi)
            
            xmi = root
        
        rep = emof.core.Repository()
        fact = emof.core.Factory(rep)
        elt = xmi.firstChild
        
       # while elt:
       #     if elt.nodeName == u'emof:Element':
       #         obj = Element(self._post, self._ids)(elt)
       #         rep.Element.append(obj)
       #     elt = elt.nextSibling
       # elt = xmi.firstChild
        while elt:
            if elt.nodeName == u'xmi:Extension':
                pass
            elt = elt.nextSibling
        elt = xmi.firstChild
        while elt:
            if elt.nodeName == u'emof:Tag':
                obj = Tag(self._post, self._ids, fact)(elt)
                #rep.Tag.append(obj)
            elt = elt.nextSibling
        elt = xmi.firstChild
        while elt:
            if elt.nodeName == u'emof:Comment':
                obj = Comment(self._post, self._ids, fact)(elt)
                #rep.Comment.append(obj)
            elt = elt.nextSibling
        elt = xmi.firstChild
        while elt:
            if elt.nodeName == u'emof:Package':
                obj = Package(self._post, self._ids, fact)(elt)
                #rep.Package.append(obj)
            elt = elt.nextSibling
        elt = xmi.firstChild
        while elt:
            if elt.nodeName == u'emof:EnumerationLiteral':
                obj = EnumerationLiteral(self._post, self._ids, fact)(elt)
                #rep.EnumerationLiteral.append(obj)
            elt = elt.nextSibling
        elt = xmi.firstChild
        while elt:
            if elt.nodeName == u'emof:Class':
                obj = Class(self._post, self._ids, fact)(elt)
                #rep.Class.append(obj)
            elt = elt.nextSibling
        elt = xmi.firstChild
        while elt:
            if elt.nodeName == u'emof:Operation':
                obj = Operation(self._post, self._ids, fact)(elt)
                #rep.Operation.append(obj)
            elt = elt.nextSibling
        elt = xmi.firstChild
        while elt:
            if elt.nodeName == u'emof:Parameter':
                obj = Parameter(self._post, self._ids, fact)(elt)
                #rep.Parameter.append(obj)
            elt = elt.nextSibling
        elt = xmi.firstChild
        while elt:
            if elt.nodeName == u'emof:Property':
                obj = Property(self._post, self._ids, fact)(elt)
                #rep.Property.append(obj)
            elt = elt.nextSibling
        elt = xmi.firstChild
        while elt:
            if elt.nodeName == u'emof:Enumeration':
                obj = Enumeration(self._post, self._ids, fact)(elt)
                #rep.Enumeration.append(obj)
            elt = elt.nextSibling
        elt = xmi.firstChild
        while elt:
            if elt.nodeName == u'emof:PrimitiveType':
                obj = PrimitiveType(self._post, self._ids, fact)(elt)
                #rep.PrimitiveType.append(obj)
            elt = elt.nextSibling
        self._postprocessing()
        return rep

# eof
